#include <iostream>
#include <boost/asio.hpp>
#include <boost/array.hpp>
#include <string>

using boost::asio::ip::udp;

// UDP 客户端和服务器示例
// 演示 UDP 通信的基本用法

class UdpServer {
private:
    boost::asio::io_context& io_context_;
    udp::socket socket_;
    udp::endpoint remote_endpoint_;
    boost::array<char, 1024> recv_buffer_;

public:
    UdpServer(boost::asio::io_context& io_context, short port)
        : io_context_(io_context),
          socket_(io_context, udp::endpoint(udp::v4(), port)) {
        start_receive();
    }

private:
    void start_receive() {
        socket_.async_receive_from(
            boost::asio::buffer(recv_buffer_), remote_endpoint_,
            [this](boost::system::error_code ec, std::size_t bytes_recvd) {
                this->handle_receive(ec, bytes_recvd);
            });
    }

    void handle_receive(const boost::system::error_code& error,
                       std::size_t bytes_transferred) {
        if (!error) {
            std::string received_data(recv_buffer_.data(), bytes_transferred);
            std::cout << "UDP Server received: " << received_data << std::endl;
            std::cout << "From: " << remote_endpoint_ << std::endl;

            // 发送回复
            std::string reply = "Echo: " + received_data;
            socket_.async_send_to(boost::asio::buffer(reply), remote_endpoint_,
                [this](boost::system::error_code ec, std::size_t /*bytes_sent*/) {
                    this->handle_send(ec);
                });
        } else {
            std::cerr << "UDP Server receive error: " << error.message() << std::endl;
        }

        start_receive();
    }

    void handle_send(const boost::system::error_code& error) {
        if (error) {
            std::cerr << "UDP Server send error: " << error.message() << std::endl;
        } else {
            std::cout << "UDP Server sent reply" << std::endl;
        }
    }
};

class UdpClient {
private:
    boost::asio::io_context& io_context_;
    udp::socket socket_;
    udp::endpoint server_endpoint_;

public:
    UdpClient(boost::asio::io_context& io_context,
              const std::string& host, const std::string& port)
        : io_context_(io_context), socket_(io_context, udp::v4()) {

        udp::resolver resolver(io_context_);
        server_endpoint_ = *resolver.resolve(udp::v4(), host, port).begin();
    }

    void send_message(const std::string& message) {
        socket_.send_to(boost::asio::buffer(message), server_endpoint_);
        std::cout << "UDP Client sent: " << message << std::endl;
    }

    std::string receive_reply() {
        boost::array<char, 1024> reply_buffer;
        udp::endpoint sender_endpoint;

        size_t reply_length = socket_.receive_from(
            boost::asio::buffer(reply_buffer), sender_endpoint);

        std::string reply(reply_buffer.data(), reply_length);
        std::cout << "UDP Client received: " << reply << std::endl;
        return reply;
    }

    void async_send_message(const std::string& message) {
        auto buffer = std::make_shared<std::string>(message);
        socket_.async_send_to(boost::asio::buffer(*buffer), server_endpoint_,
            [this, buffer](boost::system::error_code ec, std::size_t bytes_sent) {
                if (!ec) {
                    std::cout << "UDP Client async sent: " << *buffer
                             << " (" << bytes_sent << " bytes)" << std::endl;
                    this->async_receive_reply();
                } else {
                    std::cerr << "UDP Client send error: " << ec.message() << std::endl;
                }
            });
    }

private:
    void async_receive_reply() {
        auto buffer = std::make_shared<boost::array<char, 1024>>();
        auto sender_endpoint = std::make_shared<udp::endpoint>();

        socket_.async_receive_from(boost::asio::buffer(*buffer), *sender_endpoint,
            [this, buffer, sender_endpoint](boost::system::error_code ec, std::size_t bytes_recvd) {
                if (!ec) {
                    std::string reply(buffer->data(), bytes_recvd);
                    std::cout << "UDP Client async received: " << reply << std::endl;
                } else {
                    std::cerr << "UDP Client receive error: " << ec.message() << std::endl;
                }
            });
    }
};

// 广播示例
void broadcast_example() {
    try {
        std::cout << "\n=== UDP Broadcast Example ===" << std::endl;

        boost::asio::io_context io_context;
        udp::socket socket(io_context);

        socket.open(udp::v4());
        socket.set_option(boost::asio::socket_base::broadcast(true));

        udp::endpoint broadcast_endpoint(
            boost::asio::ip::address_v4::broadcast(), 12346);

        std::string message = "Broadcast message from Boost.Asio!";
        socket.send_to(boost::asio::buffer(message), broadcast_endpoint);

        std::cout << "Broadcast message sent: " << message << std::endl;

    } catch (std::exception& e) {
        std::cerr << "Broadcast error: " << e.what() << std::endl;
    }
}

// 组播示例
void multicast_example() {
    try {
        std::cout << "\n=== UDP Multicast Example ===" << std::endl;

        boost::asio::io_context io_context;

        // 组播发送者
        udp::socket sender_socket(io_context, udp::endpoint(udp::v4(), 0));
        udp::endpoint multicast_endpoint(
            boost::asio::ip::make_address_v4("239.255.0.1"), 12347);

        std::string multicast_message = "Multicast message!";
        sender_socket.send_to(boost::asio::buffer(multicast_message), multicast_endpoint);

        std::cout << "Multicast message sent: " << multicast_message << std::endl;

        // 组播接收者
        udp::socket receiver_socket(io_context);
        receiver_socket.open(udp::v4());
        receiver_socket.set_option(boost::asio::socket_base::reuse_address(true));
        receiver_socket.bind(udp::endpoint(udp::v4(), 12347));

        // 加入组播组
        receiver_socket.set_option(
            boost::asio::ip::multicast::join_group(
                boost::asio::ip::make_address_v4("239.255.0.1")));

        std::cout << "Listening for multicast messages..." << std::endl;

        boost::array<char, 1024> buffer;
        udp::endpoint sender_endpoint;

        size_t length = receiver_socket.receive_from(
            boost::asio::buffer(buffer), sender_endpoint);

        std::string received(buffer.data(), length);
        std::cout << "Multicast received: " << received << std::endl;

    } catch (std::exception& e) {
        std::cerr << "Multicast error: " << e.what() << std::endl;
    }
}

int main(int argc, char* argv[]) {
    try {
        std::cout << "=== Boost.Asio UDP Examples ===" << std::endl;

        if (argc < 2) {
            std::cout << "Usage: " << argv[0] << " <server|client|broadcast|multicast>" << std::endl;
            return 1;
        }

        std::string mode(argv[1]);

        if (mode == "server") {
            boost::asio::io_context io_context;
            UdpServer server(io_context, 12345);

            std::cout << "UDP Server listening on port 12345" << std::endl;
            std::cout << "Test with: " << argv[0] << " client" << std::endl;
            std::cout << "Press Ctrl+C to stop" << std::endl;

            io_context.run();

        } else if (mode == "client") {
            boost::asio::io_context io_context;
            UdpClient client(io_context, "127.0.0.1", "12345");

            std::cout << "UDP Client connecting to localhost:12345" << std::endl;

            // 同步发送和接收
            client.send_message("Hello UDP Server!");
            client.receive_reply();

            client.send_message("How are you?");
            client.receive_reply();

            // 异步发送和接收
            client.async_send_message("Async message!");

            io_context.run();

        } else if (mode == "broadcast") {
            broadcast_example();

        } else if (mode == "multicast") {
            multicast_example();

        } else {
            std::cout << "Invalid mode. Use: server, client, broadcast, or multicast" << std::endl;
            return 1;
        }

    } catch (std::exception& e) {
        std::cerr << "Exception: " << e.what() << std::endl;
    }

    return 0;
}
